package com.micro.download;

import com.micro.entity.FileInfo;
import com.micro.entity.RequestHeader;
import com.micro.entity.TaskInfo;
import com.micro.exception.InfoException;
import com.micro.global.ConfigInfo;
import com.micro.global.TaskInfoDataBase;
import com.micro.global.MainTaskTreadPool;
import com.micro.listener.HttpInfoListener;
import com.micro.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.BadPaddingException;
import javax.swing.*;
import java.io.File;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
 * https://www.605zy.com/?m=vod-type-id-8.html
 */

@Slf4j
public class DownloadM3u8 extends ThreadPoolExecutor implements Runnable {

    private DecimalFormat decimalFormat = new DecimalFormat("0.00");
    private static int tris = ConfigInfo.getConfigInfo().getTries()==0?255: ConfigInfo.getConfigInfo().getTries();
    /**
     * 缓存传入的任务
     */
    private TaskInfo taskInfo;
    private AtomicLong atomicLong;
    private AtomicInteger atomicInteger;
    /**
     * 关闭线程
     */
    private boolean closeThread;
    public DownloadM3u8(TaskInfo taskInfo, int coreSize) {
        super(coreSize, coreSize, 0, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), new MyThreadFactory("down-m3u8-thread-group", "down-m3u8-thread-name"));
        this.taskInfo = taskInfo;
    }

    public void setTaskInfo(TaskInfo taskInfo) {
        this.taskInfo = taskInfo;
    }

    @Override
    public void run() {
        TaskInfoDataBase.DOWNLOADING_M3U8.put(taskInfo.getUrl(), this);
        //线程调用
        try {
            start(taskInfo);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 开始下载任务
     *
     * @param taskInfo
     */
    public void start(TaskInfo taskInfo) throws IOException {
        if (taskInfo == null) {
            JOptionPane.showConfirmDialog(new JLabel("Message"), "任务信息为空！！！");
            return;
        }
        downloadTask(taskInfo);
    }

    /**
     * 下载任务
     *
     * @param taskInfo
     */
    private void downloadTask(TaskInfo taskInfo) throws IOException {
        if (taskInfo == null) {
            throw new NullPointerException("taskInfo is Null!!!");
        }
        //解析为空则解析，不为空则跳过继续
        if (taskInfo.getFileInfos() == null || taskInfo.getFileInfos().size() == 0) {
            ResolveUtil.processTask(taskInfo);
        }
        List<FileInfo> fileInfos = taskInfo.getFileInfos();
        atomicInteger = new AtomicInteger(taskInfo.getCurrent());
        atomicLong = new AtomicLong(taskInfo.getStatisticsFileSize());
        if (!fileInfos.isEmpty()) {
            taskInfo.setOperation("下载文件中...");
            fileInfos.stream().filter(fileInfo -> !fileInfo.isComplete()).forEach(fileInfo -> {
                this.execute(() -> {
                    try {
                        downloadAndDecode(taskInfo, fileInfo, taskInfo.getRequestHeader());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                });
            });
            //监控任务
            monitorWhetherTheTaskIsCompleted(taskInfo);
        } else {
            taskInfo.setOperation("地址无效，解析失败");
            return;
        }
    }
    public void close(){
        closeThread=true;
        this.getQueue().clear();
    }
    /**
     * 监控任务是否完成
     *
     * @param taskInfo
     */
    private void monitorWhetherTheTaskIsCompleted(TaskInfo taskInfo) {
        while (true) {
            try {
                if (ConfigInfo.getConfigInfo().getDownCoreSize() != this.getCorePoolSize()) {
                    this.setCorePoolSize(ConfigInfo.getConfigInfo().getDownCoreSize());
                }
                if (taskInfo.getFileInfos() == null || taskInfo.getFileInfos().size() == 0) {
                    taskInfo.setOperation("解析失败，链接无效");
                    this.shutdownNow();
                    Thread.currentThread().interrupt();
                    MainTaskTreadPool.getINSTANCE().remove(this);
                    TaskInfoDataBase.DOWNLOADING_M3U8.remove(taskInfo.getName());
                    break;
                }
                if(closeThread){
                    Thread.currentThread().interrupt();
                    this.shutdownNow();
                    break;
                }
                //当当前值等于总数时中断线程
                if (this.getActiveCount() == 0 && taskInfo.getCurrent() >= taskInfo.getTotal()) {
                    //下载状态：0、未下载; 1,准备下载 ;2、下载中... 3、停止下载 4、完成下载 5、继续 6、暂停
                    taskInfo.setOperation("所有文件下载完成，开始合并转码...");
                    try {
                        MergeUtil.merge(taskInfo.getCachePath(), "output/" + taskInfo.getName() + ".xyz");
                    } catch (Exception e) {
                        log.info("合并文件或转码文件异常：{}", e.getMessage());
                        taskInfo.setOperation("合并文件或转码文件失败");
                    }
                    //下载完成将任务从线程池移除
                    MainTaskTreadPool.getINSTANCE().remove(TaskInfoDataBase.DOWNLOADING_M3U8.get(taskInfo.getName()));
                    //从map集合移除
                    TaskInfoDataBase.DOWNLOADING_M3U8.remove(taskInfo.getName());
                    taskInfo.setOperation("任务完成！！");
                    taskInfo.setComplete(true);
                    //等待已执行的任务执行完再关闭，不接受新任务
                    this.shutdownNow();
                    break;
                }
                TimeUnit.MILLISECONDS.sleep(1000);
            } catch (InterruptedException e) {
                throw new InfoException("线程中止异常");
            }
        }
    }

    /**
     * @param taskInfo
     * @param fileInfo
     * @param requestHeader
     * @throws IOException
     */
    private void downloadAndDecode(TaskInfo taskInfo, FileInfo fileInfo, RequestHeader requestHeader) throws IOException {
        log.info("下载文件;Name:{},No.:{},url:{},tempName:{}", taskInfo.getName(), fileInfo.getNo(), fileInfo.getTsUrl(), fileInfo.getTempName());
        try {
            HttpUtil.httpGetDownload(fileInfo.getTsUrl(), requestHeader.getHeaders(), 0, new HttpInfoListener() {
                @Override
                public void setSpeed(long speed) {
                    log.info("下载速度：{}kb/s;任务名称：{}", speed,taskInfo.getName());
                    taskInfo.setSpeed(speed + "kb/s");
                }
                @Override
                public void setContentLength(long contentLength) {
                    log.info("文件大小：{};任务名称：{}", contentLength,taskInfo.getName());
                }
                @Override
                public void setBytes(byte[] bytes) {
                    try {
                        if (bytes != null && !Thread.currentThread().isInterrupted()) {
                            //解码
                            if (StringUtils.isNotBlank(fileInfo.getKey())) {
                                byte[] key = HttpUtil.httpGetBytes(fileInfo.getKey(), requestHeader.getHeaders());
                                bytes = AesUtil.decrypt(bytes, key, fileInfo.getIv());
                            }
                            taskInfo.setOperation("下载文件中......");
                            FileUtils.writeBytesToFile(taskInfo.getCachePath() + "/" + fileInfo.getTempName(), bytes);
                            taskInfo.setStatisticsFileSize(atomicLong.addAndGet(bytes.length));
                            taskInfo.setFileLength(decimalFormat.format(Double.valueOf(taskInfo.getStatisticsFileSize()) / 1024 / 1024) + "MB");
                            if (fileIsExist(taskInfo.getCachePath(), fileInfo)) {
                                countDownloadInfo(taskInfo, fileInfo);
                            }
                        } else {
                            retryDownload(taskInfo, fileInfo, requestHeader);
                        }
                    } catch (Exception e) {
                        try {
                            retryDownload(taskInfo, fileInfo, requestHeader);
                        } catch (IOException ex) {
                            ex.printStackTrace();
                        }
                    }
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
            if (e instanceof InterruptedException) {
                return;
            }
            if (e instanceof BadPaddingException) {
                taskInfo.setOperation("密钥错误");
                return;
            }
            log.info("发生异常，异常原因：{}", e.getMessage());
            try {
                retryDownload(taskInfo, fileInfo, requestHeader);
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }

    }


    /**
     * 重新下载
     *
     * @param taskInfo
     * @param fileInfo
     * @param requestHeader
     */
    private void retryDownload(TaskInfo taskInfo, FileInfo fileInfo, RequestHeader requestHeader) throws IOException {
        try {
            TimeUnit.MILLISECONDS.sleep(3000);
            log.info("重试，等待3秒");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (fileInfo.getTries() < tris  && !Thread.currentThread().isInterrupted()) {
            log.info("下载失败，重新下载;Name:{},No.:{},url:{},tempName:{}", taskInfo.getName(), fileInfo.getNo(), fileInfo.getTsUrl(), fileInfo.getTempName());
            taskInfo.setOperation("下载失败，重试下载......");
            fileInfo.setTries(fileInfo.getTries() + 1);
            downloadAndDecode(taskInfo, fileInfo, requestHeader);
        }else{
            taskInfo.setOperation("重试下载失败，停止任务......");
            Thread.currentThread().interrupt();
        }
    }

    /**
     * 计算下载进度
     *
     * @param taskInfo
     * @param fileInfo
     */
    private void countDownloadInfo(TaskInfo taskInfo, FileInfo fileInfo) {
        //设置当前下载的
        taskInfo.setCurrent(atomicInteger.incrementAndGet());
        //求进度的百分比
        double process = Double.valueOf(taskInfo.getCurrent()) / taskInfo.getTotal();
        log.info("Id:{};Name:{};TsUrl:{}", fileInfo.getNo(), taskInfo.getName(), fileInfo.getTsUrl());
        taskInfo.setProgress(String.format("%.2f", (process * 100)) + "% (" + taskInfo.getCurrent() + "/" + taskInfo.getTotal() + ")");
        fileInfo.setComplete(true);
        SeriaUtil.writeData();
    }

    /**
     * 文件是否存在
     *
     * @param cachePath
     * @param fileInfo
     * @return
     */
    private synchronized boolean fileIsExist(String cachePath, FileInfo fileInfo) {
        File file = new File(cachePath + "/" + fileInfo.getTempName());
        return file.exists() && file.isFile() && file.length() > 0;
    }


}
